import wx
from LMG.GUI.Base.menu import Menu
from LMG.Utility.constants import *

hasPopup = True
if wx.Platform == "__WXMAC__":
    hasPopup = False
    wx.PopupWindow = wx.PopupTransientWindow = wx.Window
    
SHIFT = 15
FIELDS = 5

class Field(object):
    def __init__(self, parent, index):
        self.parent = parent
        self.index = index
        
    def setText(self, text):
        text = _(self.title) + ": " + text
        return self.parent.setText(text, self.index)

    def loadTranslation(self, titleStr, helpStr):
        self.title = titleStr
        self.help = helpStr
        
class StatusBar(wx.StatusBar):
    def __init__(self, parent):
        wx.StatusBar.__init__(self, parent, -1, style = wx.ST_SIZEGRIP|wx.CLIP_CHILDREN)
        self.SetFieldsCount(FIELDS)
        self.statusbarwidths = [-1]
        self.lastfield = None
        self.popupObject = None
        self.baseId = wx.NewId()
        self.init()
        self.Bind(wx.EVT_RIGHT_DOWN, self.onRightClick)
        self.Bind(wx.EVT_LEFT_DOWN, self.onLeftClick)

    def init(self):
        self.fields = []
        for rank in range(1, FIELDS):
            self.fields.append(Field(self, rank))
            self.statusbarwidths.append(utility.config.Read("statusbar" + str(rank) + "_rank", "int"))
        self.loadTranslation()
        self.setWidths()

    def isLastField(self, cell):
        if len(self.statusbarwidths) == 1:
            return False
        if self.lastfield != None:
            return cell == self.lastfield
        for field in self.statusbarwidths[cell+1:]:
            if field != 0:
                return False
        self.lastfield = cell
        return True
        
    def setText(self, text, cell):
        self.SetStatusText(text, cell)
        newwidth = self.GetTextExtent(text)[0] + SHIFT
        if self.isLastField(cell):
            newwidth += SHIFT
        if newwidth == self.statusbarwidths[cell]:
            return False
        self.statusbarwidths[cell] = newwidth
        return True

    def clean(self):
        [field.setText("") for field in self.fields if self.statusbarwidths[field.index]]
        self.updateFields()
        
    def setWidths(self):
        self.SetStatusWidths(self.statusbarwidths)

    def showCell(self, cell):
        self.lastfield = None
        utility.config.Write("statusbar" + str(cell) + "_rank", "1")
        self.setText(self.GetStatusText(cell), cell)
        self.setWidths()

    def hideCell(self, cell):
        self.lastfield = None
        utility.config.Write("statusbar" + str(cell) + "_rank", "0")
        self.statusbarwidths[cell] = 0
        self.setWidths()

    def isShownCell(self, cell):
        return self.statusbarwidths[cell] != 0
    
    def updateFields(self):
        changed = False
        for field in self.fields:
            if not utility.frame.GUIupdate:
                return
            # Only update if this field is currently shown
            width = self.statusbarwidths[field.index]
            if (width == 0):
                continue
            if field.setText(self.getFieldText(field.index)):
                changed = True
        if changed:
            self.setWidths() 
            
    def getFieldText(self, fieldid):
        text = ""
        try:
            if fieldid == STATUSBAR_DHT:
                if utility.dht:
                    text = str(utility.dht.stats())
                else:
                    text = _("Disabled")
            elif fieldid == STATUSBAR_DLSPEED:
                text = utility.queue.getSpeed("down")
                maxdl = utility.config.Read('maxdownloadvolume', "int")
                if maxdl > 0:
                    text = "%s  (%0.2f / %d MB)" % (text, utility.queue.volume['down'] / 1048576.0, maxdl)
            elif fieldid == STATUSBAR_ULSPEED:
                text = utility.queue.getSpeed("up")
                maxup = utility.config.Read('maxuploadvolume', "int")
                if maxup > 0:
                    text = "%s (%0.2f / %d MB)" % (text, utility.queue.volume['up'] / 1048576.0, maxup)
            elif fieldid == STATUSBAR_RUNNING:
                text = "%u/%u [%u]" % (len(utility.torrents["active"]),
                                       len(utility.torrents["all"]),
                                       utility.config.Read('numsimdownload', "int"))
        except:
            pass
        return text

    def loadTranslation(self, update = False):
        self.fields[STATUSBAR_RUNNING-1].loadTranslation(_("R"), _("Running Tasks"))        
        self.fields[STATUSBAR_DHT-1].loadTranslation(_("DHT"), _("DHT nodes"))
        self.fields[STATUSBAR_DLSPEED-1].loadTranslation(_("D"), _("Download speed"))
        self.fields[STATUSBAR_ULSPEED-1].loadTranslation(_("U"), _("Upload speed"))

    def onRightClick(self, event):
        """
        Create a list of fields that are active/inactive
        """
        self.fieldpopup = Menu()
        for field in self.fields:
            self.fieldpopup.Append(self.baseId + field.index, field.help, field.help, wx.ITEM_CHECK)
            self.fieldpopup.Check(self.baseId + field.index, self.statusbarwidths[field.index]!=0)
        startid = self.baseId + self.fields[0].index
        endid = self.baseId + self.fields[-1].index
        self.Bind(wx.EVT_MENU, self.onSelectField, id=startid, id2=endid)               
        window = event.GetEventObject()
        window.PopupMenu(self.fieldpopup, event.GetPosition())

    def onSelectField(self, event):
        eventid = event.GetId()        
        field = eventid - self.baseId
        
        if self.isShownCell(field):
            self.hideCell(field)
        else:        
            self.showCell(field)
        utility.config.Flush()

    def onLeftClick(self, event):
        clickPosition = event.GetPosition()
            
        if self.GetFieldRect(STATUSBAR_RUNNING).Inside(clickPosition):
            popup = ABCNumSimPopup
        elif self.GetFieldRect(STATUSBAR_DLSPEED).Inside(clickPosition):
            popup = ABCDownloadPopup
        elif self.GetFieldRect(STATUSBAR_ULSPEED).Inside(clickPosition):
            popup = ABCUploadPopup
        else:
            popup = None
            
        if hasPopup and popup:
            if self.popupObject:
                self.popupObject.Destroy()
            self.popupObject = popup(self)
            self.popupObject.Position(self.ClientToScreen(clickPosition), (0, 0))
            self.popupObject.Popup()

        elif popup:
            dlg = wx.Dialog(utility.window, -1)
            popup(dlg)
            dlg.Fit()
            dlg.SetPosition(self.ClientToScreen(clickPosition) - (dlg.GetSize()[0], dlg.GetSize()[1]))
            dlg.ShowModal()
            dlg.Destroy()
    
        event.Skip()
            
#########################################################################

class ABCSliderPopup(wx.PopupTransientWindow):
    """
    Display a popup to control a setting
    """
    def __init__(self, parent, configvalue, titletext, checkboxText = ""):
        self.parent = parent

        style = wx.CLIP_CHILDREN|wx.RAISED_BORDER
        wx.PopupTransientWindow.__init__(self, parent, style = style)
        
        padding = 30
        
        panel = wx.Panel(self, -1, pos=(padding/2, padding/2))
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        
        self.configvalue = configvalue
               
        self.currentValue = utility.config.Read(self.configvalue, "int")
        
        sizer.Add(wx.StaticText(panel, -1, titletext), 0, wx.EXPAND)
        
        # Using dummy spacers since wx.Popups don't seem to
        # work well with sizers and alignment
        sizer.Add((10, 10), 0)
        
        # The slider and its labels get their own panel
        # so that we can Enable/Disable them as a group
        self.sliderpanel = wx.Panel(panel, -1)
        
        sliderbox = wx.BoxSizer(wx.HORIZONTAL)
        
        sliderbox.Add((10, 10), 1)
                
        style = wx.SL_VERTICAL|wx.SL_INVERSE        
        self.slider = wx.Slider(self.sliderpanel, -1, style = style)                
        sliderbox.Add(self.slider, 2, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL)
        
        sliderlabels = wx.BoxSizer(wx.VERTICAL)        
        self.toplabel = wx.StaticText(self.sliderpanel, -1, '')
        self.currentlabel = wx.StaticText(self.sliderpanel, -1, '')
        self.bottomlabel = wx.StaticText(self.sliderpanel, -1, '')
        
        sliderlabels.Add(self.toplabel, 0, wx.EXPAND)
        sliderlabels.Add((10, 10), 1)
        sliderlabels.Add(self.currentlabel, 0, wx.EXPAND)
        sliderlabels.Add((10, 10), 1)
        sliderlabels.Add(self.bottomlabel, 0, wx.EXPAND)
        
        sliderbox.Add(sliderlabels, 2, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL)
        
        self.Bind(wx.EVT_SLIDER, self.onChangeSlider, self.slider)
        
        self.sliderpanel.SetSizer(sliderbox)
        
        sizer.Add(self.sliderpanel, 0, wx.EXPAND)
        
        sizer.Add((10, 10), 0)

        if checkboxText:
            self.checkbox = wx.CheckBox(panel, -1, checkboxText)
            self.Bind(wx.EVT_CHECKBOX, self.onCheck, self.checkbox)
            sizer.Add(self.checkbox, 0, wx.EXPAND)
        else:
            self.checkbox = None
            sizer.Add((-1, 5), 0, wx.EXPAND)
        
        self.getValues()
        
        panel.SetAutoLayout(True)          
        panel.SetSizerAndFit(sizer)
                
        size = panel.GetSize()
        self.SetSize((size.width + padding, size.height + padding))
        
    def setRange(self, min, max):        
        self.slider.SetRange(min, max)
        
    def setValue(self, value):
        self.slider.SetValue(value)
        
    def getValues(self):
        raise NotImplementedError()
    
    def saveValues(self):
        if self.checkbox and self.checkbox.GetValue():
            value = 0
        else:
            value = self.slider.GetValue()
        
        # Change the current value
        if value != self.currentValue:
            utility.config.Write(self.configvalue, value)
            self.currentValue = value

    def onCheck(self, event = None):
        self.sliderpanel.Enable(not self.checkbox.GetValue())
        self.saveValues()

    def onChangeSlider(self, event = None):
        value = self.slider.GetValue()        
        self.saveValues()

class ABCRateSliderPopup(ABCSliderPopup):
    """
    Display a popup to control rate settings
    """
    def __init__(self, parent, configvalue, titletext):
        ABCSliderPopup.__init__(self, parent, configvalue, titletext, _('no limit'))
                
    def setRange(self, min, max):
        toptext = utility.speed_format((max * 1024), truncate = 0, stopearly = "KB")
        self.toplabel.SetLabel(toptext)
        bottomtext = utility.speed_format((min * 1024), truncate = 0, stopearly = "KB")
        self.bottomlabel.SetLabel(bottomtext)
        ABCSliderPopup.setRange(self, min, max)
        
    def setValue(self, value):
        currenttext = utility.speed_format((value * 1024), truncate = 0, stopearly = "KB")
        self.currentlabel.SetLabel(currenttext)
        ABCSliderPopup.setValue(self, value)
        
    def getValues(self):
        unlimited = (self.currentValue == 0)
        
        minrate = 3
        if unlimited:
            # Set default values if the rate is set to unlimited
            maxrate = 100
            current = 25
        else:
            # Make the max value dependant on the current rate,
            # but don't make it too smalle 
            maxrate = max(self.currentValue * 2, 20)
            # Make the max value a multiple of 10
            maxrate = ((maxrate/10) + 1) * 10
            current = self.currentValue
        self.setRange(minrate, maxrate)
        self.setValue(current)
        
        self.sliderpanel.Enable(not unlimited)
        self.checkbox.SetValue(unlimited)
        
    def onChangeSlider(self, event = None):
        value = self.slider.GetValue()
        
        currenttext = utility.speed_format((value * 1024), truncate = 0, stopearly = "KB")
        
        self.saveValues()
        self.currentlabel.SetLabel(currenttext)
        
class ABCUploadPopup(ABCRateSliderPopup):
    """
    Popup to control upload rate
    """
    def __init__(self, parent):        
        titletext = _('Upload Setting')
        
        if utility.torrents["downloading"]:
            # Adjust the rate for downloading
            configvalue = 'maxuploadrate'
            titletext += "\n(" + _('when downloading') + ")"
        else:
            # Adjust the rate for seeding
            configvalue = 'maxseeduploadrate'
            titletext += "\n(" + _('when seeding') + ")"
        ABCRateSliderPopup.__init__(self, parent, configvalue, titletext)

class ABCDownloadPopup(ABCRateSliderPopup):
    """
    Popup to control download rate
    """
    def __init__(self, parent):        
        titletext = _('Download Setting')
        configvalue = 'maxdownloadrate'
        ABCRateSliderPopup.__init__(self, parent, configvalue, titletext)
        
class ABCNumSimPopup(ABCSliderPopup):
    """
    Popup to control download rate
    """
    def __init__(self, parent):        
        titletext = _('Max. Number of \nActive torrents')
        configvalue = 'numsimdownload'
        ABCSliderPopup.__init__(self, parent, configvalue, titletext)
        
    def setRange(self, min, max):
        self.toplabel.SetLabel(str(max))
        self.bottomlabel.SetLabel(str(min))
        ABCSliderPopup.setRange(self, min, max)
        
    def setValue(self, value):
        self.currentlabel.SetLabel(str(value))
        ABCSliderPopup.setValue(self, value)
        
    def getValues(self):
        maxvalue = max(self.currentValue * 2, 20)
        # Make the max value a multiple of 10
        maxvalue = ((maxvalue/10) + 1) * 10
        # Make sure not more than 1000
        maxvalue = min(1000, maxvalue)

        current = self.currentValue

        self.setRange(0, maxvalue)
        self.setValue(current)
                
    def onChangeSlider(self, event = None):
        value = self.slider.GetValue()        
        self.saveValues()
        self.currentlabel.SetLabel(str(value))
